package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.domain.ProductSku;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.query.SkuQuery;
import com.spzx.product.service.IProductSkuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.product.api.domain.ProductSkuVo;
import com.spzx.product.vo.SkuPriceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品sku 服务实现类
 * </p>
 *
 * @author atguigu
 * @since 2025-02-19
 */
@Service
@Slf4j
public class ProductSkuServiceImpl extends ServiceImpl<ProductSkuMapper, ProductSku> implements IProductSkuService {

    /**
     * 畅销商品TOP20列表
     *
     * @return
     */
    @Override
    public List<ProductSkuVo> selectHotProductSkuList() {
        return baseMapper.selectHotProductSkuList();
    }

    /**
     * 根据查询条件查询商品列表
     *
     * @param skuQuery 1,3级分类 或 品牌ID 或 排序方式：销量、价格升序/降序
     * @return
     */
    @Override
    public List<ProductSkuVo> selectProductSkuList(SkuQuery skuQuery) {
        return baseMapper.selectProductSkuList(skuQuery);
    }

    @Override
    public SkuPriceVo getSkuPrice(Long skuId) {
        ProductSku sku = baseMapper.selectById(skuId);
        if (sku == null) {
            throw new ServiceException("商品不存在");
        }
        SkuPriceVo skuPriceVo = new SkuPriceVo();
        skuPriceVo.setSkuId(skuId);
        skuPriceVo.setSalePrice(sku.getSalePrice());
        skuPriceVo.setMarketPrice(sku.getMarketPrice());
        return skuPriceVo;
    }

    /**
     * 查询SPU下包含所有SKU 将SKU的规格值跟SKU映射关系封装到Map中
     *
     * @param productId
     * @return
     */
    @Override
    public Map<String, Long> getSkuSpecValueMap(Long productId) {
        Map<String, Long> map = new HashMap<>();

        LambdaQueryWrapper<ProductSku> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProductSku::getProductId, productId);
        queryWrapper.select(ProductSku::getId, ProductSku::getSkuSpec);

        List<ProductSku> skuList = baseMapper.selectList(queryWrapper);
        if (!CollectionUtils.isEmpty(skuList)) {
            for (ProductSku sku : skuList) {
                map.put(sku.getSkuSpec(), sku.getId());
            }
        }
        return map;
    }

    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 查询商品SKU信息
     *
     * @param skuId
     * @return
     */
    @Override
    public ProductSku findSkuById(Long skuId) {
        try {
            //1.优先从Redis缓存中获取业务数据，如果命中缓存直接返回即可
            //1.1 构建Redis中缓存业务数据key 形式：sku:info:skuId
            String dataKey = "sku:info:" + skuId;
            //1.2 查询Redis中业务数据，如果查询到则返回；结束
            ProductSku sku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
            if (sku != null) {
                return sku;
            }
            //可能存在大量请求都在访问某个热点key ，导致MySQL压力很大，所以采用分布式锁解决
            //2.获取分布式锁，获取锁成功，才执行业务逻辑：查询数据库、将查询结果放入到Redis缓存中  注意：业务逻辑结束释放锁
            //2.1 声明锁的名称 key 形式：业务key+后缀lock
            String lockKey = dataKey + ":lock";

            //2.2 声明锁的值，持有锁线程标识
            String lockValue = UUID.randomUUID().toString();

            //2.3 采用set EX NX 保证加锁设置过期时间原子性
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS);

            if (flag) {
                try {
                    //2.4 只有加锁成功，才执行数据库操作，将查询数据库结果放入缓存中
                    log.info("[商品服务]，{} 获取锁成功，查询商品sku信息", Thread.currentThread().getName());
                    sku = baseMapper.selectById(skuId);
                    int ttl = 7200 + new Random().nextInt(2000);
                    redisTemplate.opsForValue().set(dataKey, sku, ttl, TimeUnit.SECONDS);
                    return sku;
                } finally {
                    //2.5 业务执行结束，采用lua脚本释放锁，确保判断跟删除原子性
                    String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                            "then\n" +
                            "    return redis.call(\"del\",KEYS[1])\n" +
                            "else\n" +
                            "    return 0\n" +
                            "end";
                    DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<Boolean>(script, Boolean.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockValue);
                }
            } else {
                try {
                    log.error("[商品服务]，{} 获取锁失败，自旋...", Thread.currentThread().getName());
                    //3.获取锁失败则自旋
                    Thread.sleep(500);
                    return this.findSkuById(skuId);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        } catch (Exception e) {
            //当Redis单机部署故障，服务不可用，兜底处理方案：直接查询数据库
            log.error("[商品服务]，查询商品sku信息异常：{}", e.getMessage());
            return baseMapper.selectById(skuId);
        }
    }

    /**
     * 根据skuIdList批量查询sku价格
     * @param skuIdList
     * @return
     */
    @Override
    public List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList) {
        LambdaQueryWrapper<ProductSku> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(ProductSku::getId, skuIdList);
        queryWrapper.select(ProductSku::getId, ProductSku::getSalePrice, ProductSku::getMarketPrice);
        List<ProductSku> productSkuList = baseMapper.selectList(queryWrapper);
        if(!CollectionUtils.isEmpty(productSkuList)){
            List<SkuPriceVo> skuPriceVoList = productSkuList.stream()
                    .map(sku -> {
                        SkuPriceVo skuPriceVo = new SkuPriceVo();
                        skuPriceVo.setSkuId(sku.getId());
                        skuPriceVo.setSalePrice(sku.getSalePrice());
                        skuPriceVo.setMarketPrice(sku.getMarketPrice());
                        return skuPriceVo;
                    }).collect(Collectors.toList());
            return skuPriceVoList;
        }
        return null;
    }


   /* public static void main(String[] args) {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    threadLocal.set(finalI + "");
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.info("线程：{},私有数据：{}", Thread.currentThread().getName(), threadLocal.get());
            }, "线程：" + i).start();
        }
    }*/
}
